﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace WU.LoadTester.Lib.Service
{
    public class AuthenticationHelper
    {
        private const string expiresLabel = "ExpiresOn";
        private static string _acstoken = string.Empty;

        public static void SetCredentials(ref HttpWebRequest request, AuthSettings authSettings, string url)
        {
            //BASIC AUTH
            if (!String.IsNullOrEmpty(authSettings.Username) && authSettings.AuthenticationType == AuthType.Basic)
                request.Headers.Add("Authorization", "Basic" + " " + EncodeCredentials(authSettings));

            //INTEGRATED (WINDOWS)
            if (authSettings.AuthenticationType == AuthType.Integrated)
            {
                if(authSettings.Username != null && authSettings.Password != null)
                {
                    request.Credentials = new NetworkCredential(authSettings.Username, authSettings.Password);
                }
                else
                    request.Credentials = CredentialCache.DefaultNetworkCredentials;
            }

            //DIGEST AUTH
            if (authSettings.AuthenticationType == AuthType.Digest)
            {
                var cache = new CredentialCache();
                cache.Add(new Uri(url), "Digest", new NetworkCredential(authSettings.Username, authSettings.Password));
                request.Credentials = cache;
            }

            //ACS AUTH
            if (authSettings.AuthenticationType == AuthType.Acs)
            {
                string token = GetAcsAuthToken(authSettings);
                string token2 = string.Format("WRAP access_token=\"{0}\"", HttpUtility.UrlDecode(token));
                request.Headers.Add(token2);
            }
        }

        internal static string EncodeCredentials(AuthSettings credentials)
        {
            var strCredentials = string.Format("{0}:{1}", credentials.Username, credentials.Password);
            var encodedCredentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(strCredentials));
            return encodedCredentials;
        }

        internal static string GetAcsAuthToken(AuthSettings authSettings)
        {
            if (string.IsNullOrEmpty(_acstoken) || IsExpired(_acstoken))
            {
                WebClient client = new WebClient();
                client.BaseAddress = string.Format(authSettings.ProviderRealm);

                NameValueCollection values = new NameValueCollection();
                values.Add("wrap_name", authSettings.IdentityName);
                values.Add("wrap_password", authSettings.SymetricKey);
                values.Add("wrap_scope", authSettings.ACSName);

                // WebClient takes care of the URL Encoding
                byte[] responseBytes = client.UploadValues("WRAPv0.9", "POST", values);

                // the raw response from ACS
                string response = Encoding.UTF8.GetString(responseBytes);
                _acstoken = response
                    .Split('&')
                    .Single(value => value.StartsWith("wrap_token=", StringComparison.OrdinalIgnoreCase))
                    .Split('=')[1];
            }

            return _acstoken;
        }

        private static bool IsExpired(string swt)
        {
            try
            {
                Dictionary<string, string> nameValues = GetNameValues(swt);
                string expiresOnValue = nameValues[_acstoken];
                ulong expiresOn = Convert.ToUInt64(expiresOnValue);
                ulong currentTime = Convert.ToUInt64(GenerateTimeStamp());

                if (currentTime > expiresOn)
                {
                    return true;
                }

                return false;
            }
            catch (KeyNotFoundException)
            {
                throw new ArgumentException();
            }
        }
        public static Dictionary<string, string> GetNameValues(string token)
        {
            if (string.IsNullOrEmpty(token))
            {
                throw new ArgumentException();
            }

            return
                token
                .Split('&')
                .Aggregate(
                new Dictionary<string, string>(),
                (dict, rawNameValue) =>
                {
                    if (rawNameValue == string.Empty)
                    {
                        return dict;
                    }

                    string[] nameValue = rawNameValue.Split('=');

                    if (nameValue.Length != 2)
                    {
                        throw new ArgumentException("Invalid formEncodedstring - contains a name/value pair missing an = character");
                    }

                    if (dict.ContainsKey(nameValue[0]) == true)
                    {
                        throw new ArgumentException("Repeated name/value pair in form");
                    }

                    dict.Add(HttpUtility.UrlDecode(nameValue[0]), HttpUtility.UrlDecode(nameValue[1]));
                    return dict;
                });
        }

        private static ulong GenerateTimeStamp()
        {
            // Default implementation of epoch time
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToUInt64(ts.TotalSeconds);
        } 
    }

    public enum AuthType
    {
        Basic,
        Digest,
        Integrated,
        Acs
    }

    public class AuthSettings
    {
        public AuthType AuthenticationType { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string ACSName { get; set; }
        public string ProviderRealm { get; set; }
        public string IdentityName { get; set; }
        public string SymetricKey { get; set; }
    }
}
